React’s Secret Weapon: useSyncExternalStore for Seamless State Management
date
Mar 31, 2024
status
Published
tags
react
summary
type
Post
React’s
useSyncExternalStore
hook provides a powerful and elegant way to integrate external data sources into your React components. This hook allows your components to seamlessly react to updates outside of the typical React state management ecosystem, making it exceptionally useful for handling a wide variety of dynamic scenarios.Beyond
useState
and useEffect
In the past, synchronizing external stores with React components often involved using
useState
to manage the external value and useEffect
to synchronize that state with updates from the external source. While this approach works, it can lead to some code complexity.Let’s consider the example of tracking a user’s online status:
The
useSyncExternalStore
hook streamlines this process, as the following code snippet demonstrates:with the help of useSyncExternalStore api it becomes much easier to listen to non-react changes like:
- browser events such online, scroll, storage, geolocation, media queries, document visibility, and device battery Status
- BehaviorSubject changes from rxjs
- actor states in xstate
Essential Concepts
The
useSyncExternalStore
hook takes two key parameters:subscribe(callback)
: A function where you subscribe to the external store. Thecallback
function provided by React must be called whenever the store's state changes, signaling React to re-render the component. This callback forms a critical bridge between React and the external world.
getSnapshot()
: This function returns the current value from the external store. React uses this during the rendering process to ensure consistency.
Code Examples
Let’s explore some common use cases for
useSyncExternalStore
:Browser APIs (Network Status)
When there is a change to the internet connection, either online or offline, the callback function will be executed. This is crucial because the callback function acts as a bridge between React and external code. It must be called to notify React about the change and trigger a re-render.
Browser APIs (Storage events)
In this example, the component will update whenever you type anything into the input box.
This works because the
getSnapshot
function retrieves the current value stored in local storage under the key 'input'. The subscribe
function passes the callback function to the browser's storage event, and also returns a function to later cancel this event subscription.Importantly, we need to manually dispatch the storage event. This is because the storage event doesn’t automatically fire when a change is made in the current tab. (For more information, please see this link: https://developer.mozilla.org/en-US/docs/Web/API/Window/storage_event)."
rxjs BehaviorSubject example
The UI in this example is straightforward. You have two buttons that can either increase or decrease the value displayed above them. Each button click directly updates the value within the store.
The implementation of
useStore
leverages useSyncExternalStore
and a BehaviorSubject. We initialize a BehaviorSubject with a starting value of 0 and then connect it to React using useSyncExternalStore
. Whenever the BehaviorSubject's value changes, the subscribe
function receives the latest update. Importantly, this is where we provide the callback function to the subscribe
function.Xstate Actor example
The UI in this example remains the same as in the BehaviorSubject example. If you’re new to XState, don’t worry — its logic is designed to be intuitive.
First, we set up and create the machine. The initial ‘count’ value in the context is 0. When a ‘change’ event occurs, we simply update the count with the value passed to the event. After creating the machine, we use
createActor
to make an instance of it and initiate it with the start
method. Any subscribers will receive updated values whenever changes happen, and this is where we provide the callback function.Updating the ‘count’ value within the context is also straightforward. We send an event named ‘change’ along with the desired target value.
Conclusion
The
useSyncExternalStore
hook proves itself a powerful tool for connecting non-React state sources to your React components without the complexities of useState
and useEffect
. In the future, as you need to synchronize your application with third-party libraries, external data sources, or browser-level events, useSyncExternalStore
will be an invaluable asset – provided you have a way to get the current value and receive updates from those sources. However, remember to use this power judiciously. For simpler state interactions, useState
remains a perfectly valid solution.The code is hosted in here: matwming/react-useSyncExternalStore-example: learn how to use useSyncExternalStore with code examples (github.com)
Feel free to explore and experiment with it as much as you’d like.